﻿// Test001.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include<openssl/md5.h>
#include<stdlib.h>
#include<stdio.h>
#include<openssl/rsa.h>
#include<openssl/pem.h>
#include<openssl/aes.h>

using namespace std;

extern "C" {

#include "openssl/applink.c"

};

class Animal
{
public:
    Animal()
    {

    }
    ~Animal()
    {

    }

    virtual void speak()
    {
        cout << "animal speak" << endl;
    }
};

class Cat : public Animal
{
public:

    void speak() override
    {
        cout << "cat speak" << endl;
    }
};

void getmd5(const char* str, char* result)
{
    MD5_CTX ctx;
    MD5_Init(&ctx);
    MD5_Update(&ctx, str, strlen(str));
    unsigned char md[16] = { 0 };
    MD5_Final(md, &ctx);
    for (int i = 0; i < 16; i++)
    {
        sprintf(&result[i * 2], "%02x",md[i]);
    }
}

void generateRsaKey()
{
    RSA* rsa = RSA_new();
    BIGNUM* e = BN_new();
    BN_set_word(e, 12345);
    RSA_generate_key_ex(rsa, 1024, e, nullptr);

    //在内存中取出密钥
    RSA* pubKey = RSAPublicKey_dup(rsa);
    RSA* priKey = RSAPrivateKey_dup(rsa);

    //把密钥对写入磁盘中
#if 0
    FILE* fp = fopen("public.pem", "w");
    PEM_write_RSAPublicKey(fp,pubKey);
    fclose(fp);
    fp = fopen("private.pem", "w");
    PEM_write_RSAPrivateKey(fp, priKey,nullptr,nullptr,0,nullptr,nullptr);
    fclose(fp);

#else
    BIO* bp = BIO_new_file("public2.pem", "w");
    PEM_write_bio_RSAPublicKey(bp, rsa);
    BIO_free(bp);
    bp = BIO_new_file("private2.pem", "w");
    PEM_write_bio_RSAPrivateKey(bp, rsa, nullptr, nullptr, 0, nullptr, nullptr);
    BIO_free(bp);

#endif
}

string encryptPublicKey()
{
    //公钥加密
    string text = "让编程改变世界...";//要加密的数据
    //在磁盘中取出公钥
    RSA* pubKey=RSA_new();
    BIO* bp=BIO_new_file("public.pem", "r");
    if ((PEM_read_bio_RSAPublicKey(bp, &pubKey, nullptr, nullptr))==nullptr)
    {
        cout << "读取公钥失败了..." << endl;
        return string();
    }
    BIO_free(bp);
    //数据被加密后，长度和密钥长度相同
    int keyLen = RSA_size(pubKey);
    char* buf = new char[keyLen];
    //加密
    int len=RSA_public_encrypt(text.size(), (const unsigned char*)text.data(),
        (unsigned char*)buf,pubKey,RSA_PKCS1_PADDING);
    cout << "加密后的数据" << buf << endl;
    cout << "加密后的数据长度" << len << endl;
    return string(buf, len);
}

string decryptPrivateKey(string str)
{
    //在磁盘中取出私钥
    RSA* rsa=RSA_new();
    BIO* bp=BIO_new_file("private.pem", "r");
    if ((PEM_read_bio_RSAPrivateKey(bp, &rsa, nullptr, nullptr))==nullptr)
    {
        cout << "读取私钥失败..." << endl;
        return string();
    }
    BIO_free(bp);
    //使用私钥解密
    int keyLen = RSA_size(rsa);
    char* buf = new char[keyLen];
    int len=RSA_private_decrypt(str.size(), (const unsigned char*)str.data(),
        (unsigned char*)buf, rsa, RSA_PKCS1_PADDING);
    return string(buf,len);
}

void rsaSignAndVerfiy()
{
    //签名数据
    string text = "让编程改变世界...";
    RSA* pubKey = RSA_new();
    RSA* priKey = RSA_new();
    BIO* bp = BIO_new_file("private.pem", "r");
    if ((PEM_read_bio_RSAPrivateKey(bp, &priKey, nullptr, nullptr)) == nullptr)
    {
        cout << "私钥读取失败..." << endl;
        return;
    }
    BIO_free(bp);
    bp = BIO_new_file("public.pem", "r");
    if ((PEM_read_bio_RSAPublicKey(bp, &pubKey, nullptr, nullptr)) == nullptr)
    {
        cout << "读取公钥失败..." << endl;
        return;
    }
    int len = RSA_size(priKey);
    unsigned int outLen = 0;
    char* buf = new char[len];
    BIO_free(bp);
    RSA_sign(NID_sha1, (unsigned char*)text.data(), text.size(), (unsigned char*)buf, &outLen, priKey);
    string sigbuf(buf, outLen); //数字签名

    //验证签名
    int ret = RSA_verify(NID_sha1, (unsigned char*)text.data(), text.size(), (unsigned char*)sigbuf.data(), outLen, pubKey);
    cout << "ret= " << ret << endl;
}

void aesCBCCrypto()
{
    const char* pt = "AES是一套对称密钥的密码术，目前已广泛使用，用于替代已经不够安全的DES算法。所谓对称密钥，就是说加密和解密用的是同一个密钥，消息的发送方和接收方在消息传递前需要享有这个密钥。和非对称密钥体系不同，这里的密钥是双方保密的，不会让任何第三方知道。对称密钥加密法主要==**基于块加密**，**选取固定长度的密钥**==，去==**加密明文中固定长度的块，生成的密文块与明文块长度一样**==。显然密钥长度十分重要，块的长度也很重要。如果太短，则很容易枚举出所有的明文-密文映射；如果太长，性能则会急剧下降。==**AES中规定块长度为128 bit**==，而==**密钥长度可以选择128, 192或256 bit**== 。暴力破解密钥需要万亿年，这保证了AES的安全性。";
    const char* key = "1234567887654321";
    //准备加密要用的密钥
    AES_KEY encKey;
    AES_set_encrypt_key((const unsigned char*)key,128,&encKey);
    //计算密文长度
    int len = strlen(pt)+1;
    int length = 0;
    if (len % 16 != 0)
    {
        length = ((len / 16) + 1) * 16;
    }
    else
    {
        length = len;
    }
    //加密
    unsigned char* out = new unsigned char[length];
    unsigned char ivec[AES_BLOCK_SIZE] ;
    memset(ivec, 9, sizeof ivec);
    AES_cbc_encrypt((const unsigned char*)pt, out, length, &encKey, ivec, AES_ENCRYPT);

    //解密
    //准备解密用的密钥
    unsigned char* data = new unsigned char[length];
    memset(ivec, 9, sizeof ivec);
    AES_KEY decKey;
    AES_set_decrypt_key((const unsigned char*)key, 128, &decKey);

    AES_cbc_encrypt(out,data,length,&decKey,ivec,AES_DECRYPT);
    cout << "解密后的数据: " << data << endl;
     delete[]out;
     delete[]data;


    //// 1. 准备数据
    //const char* pt = "AES是一套对称密钥的密码术，目前已广泛使用，用于替代已经不够安全的DES算法。所谓对称密钥，就是说加密和解密用的是同一个密钥，消息的发送方和接收方在消息传递前需要享有这个密钥。和非对称密钥体系不同，这里的密钥是双方保密的，不会让任何第三方知道。\n对称密钥加密法主要==**基于块加密**，**选取固定长度的密钥**==，去==**加密明文中固定长度的块，生成的密文块与明文块长度一样**==。显然密钥长度十分重要，块的长度也很重要。如果太短，则很容易枚举出所有的明文-密文映射；如果太长，性能则会急剧下降。==**AES中规定块长度为128 bit**==，而==**密钥长度可以选择128, 192或256 bit**== 。暴力破解密钥需要万亿年，这保证了AES的安全性。";
    //// 2. 准备秘钥
    //const char* key = "1234567887654321";
    //// 3. 初始化秘钥
    //AES_KEY encKey;
    //AES_set_encrypt_key((const unsigned char*)key, 128, &encKey);
    //// 4. 加密
    //// 计算长度
    //int length = 0;
    //int len = strlen((char*)pt) + 1;
    //if (len % 16 != 0)
    //{
    //    length = ((len / 16) + 1) * 16;
    //}
    //else
    //{
    //    length = len;
    //}
    //unsigned char* out = new unsigned char[length];
    //unsigned char ivec[AES_BLOCK_SIZE];
    //memset(ivec, 9, sizeof(ivec));
    //// 密文存储在out中
    //AES_cbc_encrypt((const unsigned char*)pt, out, length, &encKey, ivec, AES_ENCRYPT);

    //// 5. 解密
    //unsigned char* data = new unsigned char[length];
    //AES_KEY deckey;
    //memset(ivec, 9, sizeof(ivec));
    //AES_set_decrypt_key((const unsigned char*)key, 128, &deckey);
    //AES_cbc_encrypt(out, data, length, &deckey, ivec, AES_DECRYPT);
    //// 6. 打印
    //cout << "还原的数据: " << data << endl;

    //delete[]out;
    //delete[]data;
}

int main()
{
    //std::cout << "Hello World!\n";
    ////cout << "again" << endl;
    //Animal* ani = new Cat;
    //ani->speak();

    //char result[33] = { 0 };
    //getmd5("hello,md5", result);
    //printf("mdt value: %s\n", result);

    //string str=encryptPublicKey();
    //cout << "解密后的数据: " << decryptPrivateKey(str) << endl;;

    /*rsaSignAndVerfiy();*/

    //aesCBCCrypto();
    
    //const int a = 10;
    //int const b = 20;
    //cout << "a= " << a << endl;
    //cout << "b= " << b << endl;
    //int* p = &a;
    //system("pause");
    //return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
